/*
 * Created by Jaren at 2023/9/25 9:22
 */
import 'dart:typed_data';

import 'package:binary/binary.dart';
import 'package:inmotion_protocol/base/codec.dart';
import 'package:inmotion_protocol/elephant/constant.dart';
import 'package:inmotion_protocol/elephant/cryptor.dart';
import 'package:inmotion_protocol/elephant/message.dart';
import 'package:inmotion_protocol/elephant/util.dart';
import 'package:inmotion_protocol/util/byte_util.dart';
import 'package:inmotion_protocol/util/int8_list_buffer.dart';
import 'package:inmotion_protocol/util/log.dart';

class ElephantDecoder extends Decoder<ElephantMessage> {
  @override
  bool check(Uint8ClampedList bytes) {
    int bytesOffset2Check = ElephantConstant.msgLengthIndex;
    int bytesLength2Check = bytes.length - ElephantConstant.msgHeadBytesLength - ElephantConstant.msgCheckBytesLength;
    int bytesCheckInfoOffset = bytes.length - ElephantConstant.msgCheckBytesLength;
    return ByteUtil.arrayEquals(
        // 本地计算的校验和
        ElephantUtil.calSumCheck(bytes, offset: bytesOffset2Check, length: bytesLength2Check),
        // 期望的校验和
        bytes,
        // bytes中期望校验和的offset
        list2Offset: bytesCheckInfoOffset);
  }

  @override
  void decrypt(Int8ListBuffer bytes) {
    int olderReadIndex = bytes.readerIndex;
    int olderWriteIndex = bytes.writerIndex;

    bytes.readerIndex = ElephantConstant.msgCryptIndex;
    int cryptIndex = ByteUtil.byte2UInt(bytes.readByte()); // cryptIndex即CRYPT_TABLE索引值

    Uint8ClampedList decryptedData = Uint8ClampedList(bytes.readableBytes());
    for (int i = 0; bytes.isReadable(); i++) {
      cryptIndex %= 0x100;
      decryptedData[i] = bytes.readByte() ^ Cryptor.cryptPwdBook[cryptIndex++];
    }
    // 从crypt位后开始解密
    bytes.writerIndex = ElephantConstant.msgCryptIndex + 1;
    for (int i = 0, length = decryptedData.length; i < length; i++) {
      bytes.writeByte(decryptedData[i]);
    }

    bytes.readerIndex = olderReadIndex;
    bytes.writerIndex = olderWriteIndex;
  }

  @override
  int findPacketHead(Uint8ClampedList bytes) {
    for (int i = 0, length = bytes.length - ElephantConstant.msgHeadBytesLength; i < length; i++) {
      if (ByteUtil.arrayEquals(bytes, ElephantConstant.msgHeadBytes, list1Length: ElephantConstant.msgHeadBytesLength)) return i;
    }
    return -1;
  }

  @override
  int findValidPacketEndIndex(Int8ListBuffer buffer) {
    int olderReadIndex = buffer.readerIndex;
    int olderWriteIndex = buffer.writerIndex;
    int totalLength = buffer.readableBytes();

    buffer.readerIndex = ElephantConstant.msgLengthIndex;

    int length = ByteUtil.byte2UInt(buffer.readByte());

    buffer.readerIndex = olderReadIndex;
    buffer.writerIndex = olderWriteIndex;

    return totalLength - (ElephantConstant.msgHeadBytesLength + 1 + 1 + 1 + length + ElephantConstant.msgCheckBytesLength);
  }

  @override
  bool isStartWithPacketHead(Uint8ClampedList bytes) {
    return ByteUtil.arrayEquals(bytes, ElephantConstant.msgHeadBytes, list1Length: 2, list2Length: 2);
  }

  @override
  int minMsgLength() {
    return ElephantConstant.msgMinLength;
  }

  @override
  ElephantMessage? parse(Uint8ClampedList buf) {
    try {
      int offset = ElephantConstant.msgHeadBytesLength;
      Uint8 length = ByteUtil.toUint8(buf[offset++]);
      Uint8 encrypt = ByteUtil.toUint8(buf[offset++]);
      Uint8 device = ByteUtil.toUint8(buf[offset++]);
      Uint8 function = ByteUtil.toUint8(buf[offset++]);
      Uint8 extra = ByteUtil.toUint8(buf[offset++]);
      Uint8ClampedList data = buf.sublist(offset, buf.length - 2);
      offset += data.length;
      Uint8ClampedList check = buf.sublist(offset);
      return ElephantMessage.create(length, encrypt, device, function, extra, data, check);
    } catch (e) {
      LogUtils.e("Parse failed!", e);
      return null;
    }
  }

  @override
  Uint8ClampedList unescape(Uint8ClampedList bytes) {
    return bytes;
  }
}

class ElephantEncoder extends Encoder<ElephantMessage> {
  @override
  Uint8ClampedList encrypt(Uint8ClampedList bytes) {
    int cryptIndex = ByteUtil.byte2UInt(bytes[ElephantConstant.msgCryptIndex]);

    for (int i = ElephantConstant.msgCryptIndex + 1, length = bytes.length; i < length; i++) {
      cryptIndex %= 0x100;
      bytes[i] = (bytes[i] ^ Cryptor.cryptPwdBook[cryptIndex++]);
    }
    return bytes;
  }

  @override
  Uint8ClampedList msgToBytes(ElephantMessage msg) {
    // 消息总长度
    int totalLength = msg.totalLength();

    Uint8ClampedList bytes = Uint8ClampedList(totalLength);
    int offset = 0;
    for (; offset < ElephantConstant.msgHeadBytesLength; offset++) {
      bytes[offset] = msg.head[offset];
    }
    bytes[offset++] = msg.length.value;
    bytes[offset++] = msg.encrypt.value;
    bytes[offset++] = msg.device.value;
    bytes[offset++] = msg.function.value;
    bytes[offset++] = msg.extra.value;
    ByteUtil.copy(msg.data, 0, bytes, offset, msg.data.length);
    offset += msg.data.length;
    ByteUtil.copy(msg.check, 0, bytes, offset, msg.check.length);
    // offset += msg.check.length;
    return bytes;
  }

  @override
  bool needEncrypt(ElephantMessage msg) {
    return true;
  }
}
